﻿/*
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.

Copyright (C) 2005-2014  Sylvain Rougeaux (Cyber Sinh) (http://www.luminescence-software.org/)
*/

using System;
using System.IO;
using System.Linq;
using System.Text;

namespace Luminescence.Xiph
{
   /// <summary>
   ///   Classe permettant le lecture séquencielle de pages Ogg dans un flux.
   /// </summary>
   public class OggPageReader : OggPage, IDisposable
   {
      private readonly BinaryReader br;

      /// <summary>
      ///   Indique si un flux multiplexé a été détecté à ce stade du parcours dans le flux Ogg.
      /// </summary>
      public bool MultiplexedStreamDetected { get; private set; }

      private long offset;

      /// <summary>
      ///   Position dans le flux.
      /// </summary>
      public long Offset
      {
         get { return offset; }
         set { offset = value; }
      }

      /// <summary>
      ///   Constructeur de la classe OggPageReader.
      /// </summary>
      /// <param name = "stream">Flux contenant les pages Ogg à lire</param>
      public OggPageReader(Stream stream)
      {
         br = new BinaryReader(stream);
         ReadNextPage();
      }

      /// <summary>
      ///   Lit la première page Ogg du flux.
      /// </summary>
      public void ReadFirstPage()
      {
         offset = 0;
         ReadNextPage();
      }

      /// <summary>
      ///   Lit la dernière page Ogg du flux.
      /// </summary>
      public void ReadLastPage()
      {
         long index = Math.Max(br.BaseStream.Length - MaxPageLength, 0);
         br.BaseStream.Position = index;
         string page = Encoding.ASCII.GetString(br.ReadBytes(index > 0 ? MaxPageLength : (int)br.BaseStream.Length));
         offset = page.LastIndexOf("OggS") + index;
         ReadNextPage();
      }

      /// <summary>
      ///   Lit la page suivante dans le flux Ogg.
      /// </summary>
      /// <returns>True si la fin du flux a été atteinte, sinon False</returns>
      public bool ReadNextPage()
      {
         if (offset >= br.BaseStream.Length)
            return false;

         br.BaseStream.Position = offset;

         if (!OggS.SequenceEqual(br.ReadBytes(4)))
            throw new FileFormatException("Ogg page not found.");

         if (br.ReadByte() != 0)
            throw new FileFormatException("Stream structure version unsupported.");

         HeaderType = (OggHeaderType)br.ReadByte();

         AbsoluteGranulePostion = br.ReadInt64();
         int serial = br.ReadInt32();
         if (!MultiplexedStreamDetected && StreamSerialNumber != 0 && StreamSerialNumber != serial)
            MultiplexedStreamDetected = true;
         StreamSerialNumber = serial;
         PageSequenceNumber = br.ReadInt32();
         Checksum = br.ReadUInt32();

         byte nbSegments = br.ReadByte();
         Segments = new byte[nbSegments][];
         var sizeSegments = new byte[nbSegments];
         for (int i = 0; i < nbSegments; i++)
            sizeSegments[i] = br.ReadByte();

         for (int j = 0; j < sizeSegments.Length; j++)
            Segments[j] = br.ReadBytes(sizeSegments[j]);

         offset = br.BaseStream.Position;

         return true;
      }

      #region Membres IDisposable

      /// <summary>
      ///   Nettoyage des ressources utilisées par la classe.
      /// </summary>
      /// <param name = "disposing">True si les ressources managées doivent être libérées, sinon False</param>
      protected virtual void Dispose(bool disposing)
      {
         if (disposing)
            br.Close();
      }

      /// <summary>
      ///   Nettoyage des ressources utilisées par la classe.
      /// </summary>
      public void Dispose()
      {
         Dispose(true);
         GC.SuppressFinalize(this);
      }

      #endregion
   }
}